package cheme.ProcessModel.Interpreter;

import java.beans.PropertyChangeEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import cheme.ProcessModel.IParent;
import cheme.ProcessModel.IPropertyAware;
import cheme.ProcessModel.PropertyAwareObject;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.ITagComponent;

public class LookupTable extends PropertyAwareObject
{
	/*
	 * All model interpretable elements (Tag components and equations) are accessible by reference to the object
	 */
	private static Map<Object, ModelElementData> objectToData;
	
	/*
	 * Tag components (representing variable in the system) can be accessed to by name as well
	 */
	private static Map<String, ModelElementData> stringToData;
	
	private Map<Object, ModelElementData> getObjectToData()
	{
		if(null == objectToData)
		{
			objectToData = new HashMap<Object, ModelElementData>();
		}
		
		return objectToData;
	}
	
	private Map<String, ModelElementData> getStringToData()
	{
		if(null == stringToData)
		{
			stringToData = new HashMap<String, ModelElementData>();
		}
		
		return stringToData;
	}
	
	public void print()
	{
		Iterator<ModelElementData> itr = iterator();
		
		//System.out.println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-");
		while(itr.hasNext())
		{
			//System.out.println(itr.next().getModel().toString());
		}
		//System.out.println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-");
	}

	public ModelElementData get(Object modelElement)
	{
		return getObjectToData().get(modelElement);
	}
	
	public ModelElementData get(String modelElementName)
	{
		return getStringToData().get(modelElementName);
	}
	
	public void put(Object modelElement, ModelElementData modelElementData)
	{
		if(null == modelElement)
		{
			//System.out.println("Null variable.");
		}
		
		//System.out.print("Adding element reference: " + modelElement.toString());
		
		if(modelElement instanceof ITagComponent)
		{
			//System.out.print(" " + (String)((ITagComponent)modelElement).getLabel() + 
								//" to the lookup table.");
			
			put((String)((ITagComponent)modelElement).getLabel(), modelElementData);
			((IParent)modelElement).get(ITagComponent.LABEL_INDEX).addPropertyChangeListener(this);
		}
		
		//System.out.println();
		
		getObjectToData().put(modelElement, modelElementData);
	}
	
	protected void put(String modelElementName, ModelElementData modelElementData)
	{
		//System.out.println("Adding name reference " + modelElementName + " to the lookup table.");
		
		getStringToData().put(modelElementName, modelElementData);
		if(null != modelElementData)
		{		
			firePropertyChange(P_ELEMENT_ADDED, null, modelElementData.getModel());
		}
	}
		
	/*
	 * WIll remove refrence to the object from both the element map and the string map
	 */
	public void remove(Object modelElement)
	{
		//System.out.println("Removing element reference from the lookup table.");
		
		if(modelElement instanceof ITagComponent)
		{
			remove(((ITagComponent)modelElement).getLabel());
			((IParent)modelElement).get(ITagComponent.LABEL_INDEX).removePropertyChangeListener(this);
		}
				
		getObjectToData().remove(modelElement);
		
	}
	
	/*
	 * Only removes the string map, not the element map
	 */
	protected void remove(String modelElementName)
	{
		//System.out.println("Removing name reference to " + modelElementName + " from the lookup table.");
		
		ModelElementData data = get(modelElementName);
		
		getStringToData().remove(modelElementName);

		if (null != data){
			firePropertyChange(P_ELEMENT_REMOVED, data.getModel(), null);
		}
	}
	
	@Override
	protected void handleTextChange(PropertyChangeEvent evt)
	{
		if(evt.getPropertyName().equals(IPropertyAware.P_TEXT))
		{
			//System.out.println("LOOKUPTABLE handleTextChange P_TEXT");
			
			String name = (String)evt.getOldValue();
			ModelElementData modelElementData = get(name);
			
			/*
			 * only need to remove the entry in the name map here
			 */
			remove(name);
			name = (String)evt.getNewValue();
			
			/*
			 * And add a new entry for he new name here
			 */
			put(name, modelElementData);
			if(null != modelElementData){
				firePropertyChange(P_ELEMENT_NAME_UPDATED, null, modelElementData.getModel());
			}
		}
		
		super.handleTextChange(evt);
	}
	
	public boolean contains(Object modelElement)
	{
		return getObjectToData().containsKey(modelElement);
	}
	
	public boolean contains(String modelElementName)
	{
		return getStringToData().containsKey(modelElementName);
	}
	
	public Iterator<ModelElementData> iterator()
	{
		return getObjectToData().values().iterator();
	}
}
